package br.com.loja.model.service;

import java.util.List;
import java.util.Map;
import javax.persistence.Cacheable;
import br.com.loja.model.dao.IDAO;
import br.com.loja.model.dao.factory.IDAOFactory;
import br.com.loja.model.entity.Switchable;
import br.com.loja.model.service.callback.*;

/**
 *
 * @author David Luiz
 * @param <T>Classe que implementa uma entidade observavel
 */
public class Service<T> {

    protected final IDAO<T> entityDAO;
    private final Class<T> clazz;
    private final CallerCallback<T> caller;

    public Service(IDAOFactory factory, Class<T> clazz) {
        this.entityDAO = factory.getEntityDAO(clazz);
        this.clazz = clazz;
        this.caller = new CallerCallback<>(clazz);
    }

    public List<T> list() {
        return entityDAO.list(isCacheable());
    }

    private boolean isCacheable() {
        Cacheable cacheable = clazz.getAnnotation(Cacheable.class);
        if (cacheable == null) {
            return false;
        }
        return Boolean.valueOf(true).equals(cacheable.value());
    }

    public List<T> list(Map<String, ?> properties, boolean cacheable) {
        return entityDAO.list(properties, cacheable);
    }

    public synchronized void save(T entity) {
        executeSave(entity);
    }

    private void executeSave(T entity) {
        executeCallbackPreSave(entity);
        entityDAO.save(entity);
        executeCallbackPostSave(entity);
    }

    private void executeCallbackPostSave(T entity) {
        caller.executeCallback(entity, PostPersist.class, PostService.class);
    }

    private void executeCallbackPreSave(T entity) {
        caller.executeCallback(entity, PrePersist.class, PreService.class);
    }

    public void remove(T entity) {
        executeRemove(entity);
    }

    private void executeRemove(T entity) {
        executeCallbackPreRemove(entity);
        if (isSwitchable(entity)) {
            offEntity((Switchable) entity);
            entityDAO.update(entity);
        } else {
            entityDAO.remove(entity);
        }
        executeCallbackPostRemove(entity);
    }

    private void offEntity(Switchable entity) {
        entity.setDead(true);
    }

    private boolean isSwitchable(T entity) {
        return entity instanceof Switchable;
    }

    private void executeCallbackPostRemove(T entity) {
        caller.executeCallback(entity, PostRemove.class, PostService.class);
    }

    private void executeCallbackPreRemove(T entity) {
        caller.executeCallback(entity, PreRemove.class, PreService.class);
    }

    public T findByPrimaryKey(Object pk) {
        return entityDAO.findByPrimaryKey(pk);
    }

    public synchronized void update(T... entities) {
        if (entities != null) {
            for (T entity : entities) {
                executeUpdate(entity);
            }
        }
    }

    private void executeUpdate(T entity) {
        executeCallbackPreUpdate(entity);
        entityDAO.update(entity);
        executeCallbackPostUpdate(entity);
    }

    private void executeCallbackPostUpdate(T entity) {
        caller.executeCallback(entity, PostUpdate.class, PostService.class);
    }

    private void executeCallbackPreUpdate(T entity) {
        caller.executeCallback(entity, PreUpdate.class, PreService.class);
    }

}
